﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Should.Fluent;
using System.Linq;

namespace Is.Test
{
    public partial class IsObjectExtensionsTest
    {
        [TestMethod]
        public void Test_Equal_Array()
        {
            string[] expected = { "@", "a", "ab", "b" };
            string[] actual = { "@", "a", "ab", "b" };
            expected.Is().Equal(actual).Should().Be.True();
            expected.Is().Not.Equal(actual).Should().Be.False();

            expected = new string[] { "@", "a", "ab", "b", "c" };
            actual = new string[] { "@", "a", "ab", "b" };
            expected.Is().Equal(actual).Should().Be.False();
            expected.Is().Not.Equal(actual).Should().Be.True();

            expected = new string[] { "@", "d", "v", "d" };
            actual = new string[] { "@", "a", "ab", "b" };
            expected.Is().Equal(actual).Should().Be.False();
            expected.Is().Not.Equal(actual).Should().Be.True();

            expected = new string[] { null, "hello" };
            actual = new string[] { null, "world" };
            expected.Is().Equal(actual).Should().Be.False();
            expected.Is().Not.Equal(actual).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Number()
        {
            Double.NegativeInfinity.Is().Equal(Double.NegativeInfinity).Should().Be.True();
            1.23.Is().Equal(Double.NegativeInfinity).Should().Be.False();
            Double.PositiveInfinity.Is().Equal(Double.PositiveInfinity).Should().Be.True();
            1.23.Is().Equal(Double.PositiveInfinity).Should().Be.False();
            Double.NegativeInfinity.Is().Equal(Double.PositiveInfinity).Should().Be.False();
            Double.NaN.Is().Equal(1.234).Should().Be.False();
            1.234.Is().Equal(Double.NaN).Should().Be.False();
            Double.NaN.Is().Equal(Double.NaN).Should().Be.True();
            1.0.Is().Equal(1.0, 0.0).Should().Be.True();
            1.1.Is().Equal(1.2, 0.1).Should().Be.True();
            1.1.Is().Equal(1.3, 0.1).Should().Be.False();
            1.0.Is().Not.Equal(1.0, 0).Should().Be.False();
            1.1.Is().Not.Equal(1.2, 0.1).Should().Be.False();
            1.1.Is().Not.Equal(1.3, 0.1).Should().Be.True();
            
            decimal expecteddecimal = 25;
            decimal actualdecimal = 42;
            expecteddecimal.Is().Equal(actualdecimal).Should().Be.False();

            double expecteddouble = 25.3;
            double actualdouble = 42.0;
            expecteddouble.Is().Equal(actualdouble).Should().Be.False();

            Single.NegativeInfinity.Is().Equal(Single.NegativeInfinity).Should().Be.True();
            1.23f.Is().Equal(Single.NegativeInfinity).Should().Be.False();
            1.23f.Is().Equal(Single.PositiveInfinity).Should().Be.False();
            Single.PositiveInfinity.Is().Equal(Single.PositiveInfinity).Should().Be.True();
            Single.NegativeInfinity.Is().Equal(Single.PositiveInfinity).Should().Be.False();
            2L.Is().Equal(2L).Should().Be.True();
            3L.Is().Equal(2L).Should().Be.False();

            UInt64 expectedUInt64 = 25;
            UInt64 actualUInt64 = 42;
            expectedUInt64.Is().Equal(actualUInt64).Should().Be.False();
        }

        [TestMethod]
        public void Test_Equal_IEnumerable()
        {
            IEnumerable<int> items = IntGenerator.Range(1, 12);
            IEnumerable<int> others = IntGenerator.Range(1, 12);

            items.Is().Equal(others).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Null()
        {
            object nullobject = null;
            nullobject.Is().Equal(null).Should().Be.True();

            new object().Is().Equal(null).Should().Be.False();

            nullobject.Is().Equal(new object()).Should().Be.False();
        }

        [TestMethod]
        public void Test_Equal_Byte()
        {
            byte valueType = 35;
            Byte referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<byte>().Equal(35).Should().Be.True();
            referenceValue.Is<byte>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Decimal()
        {
            decimal valueType = 35;
            Decimal referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<decimal>().Equal(35).Should().Be.True();
            valueType.Is().Equal(35M).Should().Be.True();
            referenceValue.Is<decimal>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Int16()
        {
            short valueType = 35;
            Int16 referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<short>().Equal(35).Should().Be.True();
            referenceValue.Is<short>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Int32()
        {
            int valueType = 35;
            Int32 referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is().Equal(35).Should().Be.True();
            referenceValue.Is().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Int64()
        {
            long valueType = 35;
            Int64 referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<long>().Equal(35).Should().Be.True();
            referenceValue.Is<long>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_SByte()
        {
            sbyte valueType = 35;
            SByte referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<sbyte>().Equal(35).Should().Be.True();
            referenceValue.Is<sbyte>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_UInt16()
        {
            ushort valueType = 35;
            UInt16 referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<ushort>().Equal(35).Should().Be.True();
            referenceValue.Is<ushort>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_UInt32()
        {
            uint valueType = 35;
            UInt32 referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<uint>().Equal(35).Should().Be.True();
            referenceValue.Is<uint>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_UInt64()
        {
            ulong valueType = 35;
            UInt64 referenceValue = 35;

            (valueType == referenceValue).Should().Be.True();
            referenceValue.Is().Equal(valueType).Should().Be.True();
            valueType.Is<ulong>().Equal(35).Should().Be.True();
            referenceValue.Is<ulong>().Equal(35).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_Int32Int64Comparison()
        {
            long l64 = 0;
            int i32 = 0;
            l64.Is<long>().Equal(i32).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_IntegerLongComparison()
        {
            1L.Is<long>().Equal(1).Should().Be.True();
            long i = 1;
            i.Is<long>().Equal(1L).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_String()
        {
            "expected".Is().Equal("actual").Should().Be.False();

            string testString = "Test String";
            string expected = testString;
            string actual = testString;

            (actual == expected).Should().Be.True();
            expected.Is().Equal(actual).Should().Be.True();
            "foo".Is().Equal("foo\0").Should().Be.False();

            expected = "TestString";
            actual = "testString";

            (actual == expected).Should().Be.False();
            expected.Is().Not.Equal(actual).Should().Be.True();
            expected.Is().Equal(actual, StringComparer.CurrentCultureIgnoreCase).Should().Be.True();

            string s1 = "test";
            string s2 = new StringBuilder(s1).ToString();

            (s1.Equals(s2)).Should().Be.True();
            s2.Is().Equal(s1).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_NullableValueTypesCanBeNull()
        {
            DateTime? dt1 = null;
            DateTime? dt2 = null;

            dt1.Is().Equal(dt2).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_CallsIEquatable()
        {
            EquatableObject obj1 = new EquatableObject(1);
            EquatableObject obj2 = new EquatableObject(1);

            obj1.Equals(obj2).Should().Be.True();
            obj1.Is().Equal(obj2).Should().Be.True();
            

            obj1.Equals__Called.Should().Be.True();
            obj2.Equals__Called.Should().Be.False();
            obj1.Is().SameAs(obj2.Equals_Other).Should().Be.False();
            obj2.Is().SameAs(obj1.Equals_Other).Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_When_comparing_derived_IEquitable_object_Should_equate()
        {
            EquatableObject baseObj = new EquatableObject(1);
            DerivedEquatableObject derivedObj = new DerivedEquatableObject(1,2);

            baseObj.Is().Equal(derivedObj).Should().Be.True();
            baseObj.Equals__Called.Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_When_comparing_base_Generic_IComparable_object_Should_equate()
        {
            EquatableObject baseObj = new EquatableObject(1);
            DerivedEquatableObject derivedObj = new DerivedEquatableObject(1,2);

            derivedObj.Is<EquatableObject>().Equal(baseObj).Should().Be.True();
            derivedObj.Equals__Called.Should().Be.True();
        }

        [TestMethod]
        public void Test_Equal_When_comparing_object_with_default_equality_behavior_to_derived_class_Should_fail()
        {
            var obj = new DefaultEqualityObject();
            var derivedObj = new DerivedDefaultEqualityObject();
            obj.Is().Equal(derivedObj).Should().Be.False();
        }

        
    }
}
